/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.icee.myth.server.battle;

import com.google.protobuf.ByteString;
import com.icee.myth.protobuf.ExternalCommonProtocol.BattleActionProto;
import com.icee.myth.protobuf.ExternalCommonProtocol.BattleMessageProto;
import com.icee.myth.protobuf.ExternalCommonProtocol.BattleResultProto;
import com.icee.myth.protobuf.ExternalCommonProtocol.BattleRoundProto;
import com.icee.myth.server.fighter.Fighter;
import com.icee.myth.server.reward.RewardItemInfo;
import com.icee.myth.utils.Consts;
import com.icee.myth.utils.RandomGenerator;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * 
 * @author liuxianke
 */
public abstract class Battle implements Runnable {
    public final Fighter[] fighters = new Fighter[12];

    public final boolean needBuildProto;
    protected BattleActionProto.Builder battleActionBuilder;
    protected BattleRoundProto.Builder battleRoundBuilder;
	protected BattleResultProto.Builder battleResultBuilder;

    public LinkedList<RewardItemInfo> rewardItemInfos;

    public LinkedList<ChangeCardInfo> changeCardInfos;

    public Battle(boolean needBuildProto) {
        this.needBuildProto = needBuildProto;
    }

    public void addFighter(Fighter fighter) {
        assert (fighters[fighter.id] == null);
        fighters[fighter.id] = fighter;
    }

    public void addChangeCardInfo(ChangeCardInfo changeCardInfo) {
        if (changeCardInfos == null) {
            changeCardInfos = new LinkedList<ChangeCardInfo>();
        }

        changeCardInfos.add(changeCardInfo);
    }

    /**
     * 计算战斗过程和结果
     * @return 胜利方
     */
    public int calculateBattleResult() {
        if (needBuildProto) {
            battleResultBuilder = BattleResultProto.newBuilder();

            for (Fighter fighter : fighters) {
                if (fighter != null) {
                    battleResultBuilder.addFighters(fighter.buildFighterProto());
                }
            }

            if (changeCardInfos != null) {
                for (ChangeCardInfo changeCardInfo : changeCardInfos) {
                    battleResultBuilder.addChangeCards(changeCardInfo.buildChangeCardProto());
                }
            }
        }

//        // 计算战斗结果
//        // 确定先手方
//        int totalLeftDex = 0;
//        for (int i=0; i<6; i++) {
//            if (fighters[i] != null) {
//                totalLeftDex += fighters[i].dex;
//            }
//        }
//
//        int totalRightDex = 0;
//        for (int i=6; i<12; i++) {
//            if (fighters[i] != null) {
//                totalRightDex += fighters[i].dex;
//            }
//        }

        int currentRound = 0;
        int currentId = 0;
        boolean needStartBattleRound = true;
        boolean isLeftDie = false;
        boolean isRightDie = false;
        // 进入模拟战斗循环
        do {
            if (needStartBattleRound) {
                if (needBuildProto) {
                    startBattleRound(currentRound);
                }
                
                needStartBattleRound = false;
            }
            
//            if (totalLeftDex >= totalRightDex) {
                // 本方（左方先手）
                if ((fighters[currentId] != null) && (fighters[currentId].curHp > 0)) {
                    fighters[currentId].action();
                }
                //敌方（右手方）
                if ((fighters[currentId+6] != null) && (fighters[currentId+6].curHp > 0)) {
                    fighters[currentId+6].action();
                }
//            } else {
//                // 右方先手
//                if ((fighters[currentId+6] != null) && (fighters[currentId+6].curHp > 0)) {
//                    fighters[currentId+6].action();
//                }
//
//                if ((fighters[currentId] != null) && (fighters[currentId].curHp > 0)) {
//                    fighters[currentId].action();
//                }
//            }

            isLeftDie = isAllLeftTeamDie();
            isRightDie = isAllRightTeamDie();
            currentId++;
            if ((currentId == 6) || isLeftDie || isRightDie){
                // 回合结束
                if (needBuildProto) {
                    endBattleRound();
                }

                currentId = 0;
                currentRound++;
                needStartBattleRound = true;
            }
        } while(!(isLeftDie || isRightDie) && (currentRound<Consts.MAX_BATTLE_ROUND));

        int winner = isRightDie?0:(isLeftDie?1:-1);
        if (needBuildProto) {
            battleResultBuilder.setWinner(winner);
        }

        return winner;
    }

    public void addRewardItemInfo(RewardItemInfo rewardItemInfo) {
        if (rewardItemInfos == null) {
            rewardItemInfos = new LinkedList<RewardItemInfo>();
        }

        rewardItemInfos.add(rewardItemInfo);
    }

    public boolean isAllLeftTeamDie() {
        for (int i=0; i<6; i++) {
            if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                return false;
            }
        }
        return true;
    }

    public boolean isAllRightTeamDie() {
        for (int i=6; i<12; i++) {
            if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                return false;
            }
        }
        return true;
    }

    public void startBattleRound(int currentRound) {
        battleRoundBuilder = BattleRoundProto.newBuilder();
        battleRoundBuilder.setRound(currentRound);
    }

    public void startBattleAction() {
        battleActionBuilder = BattleActionProto.newBuilder();
    }

    public void addBattleMessage(int msgType, ByteString data) {
        battleActionBuilder.addBattleMessages(buildBattleMessageProto(msgType, data));
    }

    public void endBattleAction() {
        battleRoundBuilder.addBattleActions(battleActionBuilder);
        battleActionBuilder = null;
    }

    public void endBattleRound() {
        battleResultBuilder.addBattleRounds(battleRoundBuilder);
        battleRoundBuilder = null;
    }

    public BattleResultProto getBattleResult() {
        return battleResultBuilder.build();
    }
    
    public LinkedList<Fighter> getRandomTargets(Fighter fighter, int num){
        LinkedList<Fighter> targets = new LinkedList<Fighter>();
        LinkedList<Fighter> selectList = new LinkedList<Fighter>();

        int startId = (fighter.id<6)?0:6;
        for (int i=0; i<6; i++) {
            Fighter fighter1 = fighters[startId + i];
            if ((fighter1 != null) && (fighter1.curHp > 0)) {
                selectList.add(fighter1);
            }
        }

        if (selectList.size() <= num) {
            targets.addAll(selectList);
        } else {
            for (int i=0; i<num; i++) {
                targets.add(selectList.remove(RandomGenerator.INSTANCE.generator.nextInt(selectList.size())));
            }
        }

        return targets;
    }

    public LinkedList<Fighter> getTargetsInTeam(Fighter fighter) {
        LinkedList<Fighter> targets = new LinkedList<Fighter>();

        if (fighter.id < 6) {
            for (int i=0; i<6; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }
        } else {
            for (int i=6; i<12; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }
        }

        return targets;
    }

    public LinkedList<Fighter> getTargetsInCross(Fighter fighter) {
        LinkedList<Fighter> targets = new LinkedList<Fighter>();

        if (fighter.id < 3) {
            for (int i= 0; i<3; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }

            if ((fighters[fighter.id + 3] != null) && (fighters[fighter.id + 3].curHp > 0)) {
                targets.add(fighters[fighter.id + 3]);
            }
        } else if (fighter.id < 6) {
            for (int i= 3; i<6; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }

            if ((fighters[fighter.id - 3] != null) && (fighters[fighter.id - 3].curHp > 0)) {
                targets.add(fighters[fighter.id - 3]);
            }
        } else if (fighter.id < 9) {
            for (int i= 6; i<9; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }

            if ((fighters[fighter.id + 3] != null) && (fighters[fighter.id + 3].curHp > 0)) {
                targets.add(fighters[fighter.id + 3]);
            }
        } else if (fighter.id < 12) {
            for (int i= 9; i<12; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }

            if ((fighters[fighter.id - 3] != null) && (fighters[fighter.id - 3].curHp > 0)) {
                targets.add(fighters[fighter.id - 3]);
            }
        }

        return targets;
    }

    public LinkedList<Fighter> getTargetsInRow(Fighter fighter) {
        LinkedList<Fighter> targets = new LinkedList<Fighter>();

        if (fighter.id < 3) {
            for (int i= 0; i<3; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }
        } else if (fighter.id < 6) {
            for (int i= 3; i<6; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }
        } else if (fighter.id < 9) {
            for (int i= 6; i<9; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }
        } else if (fighter.id < 12) {
            for (int i= 9; i<12; i++) {
                if ((fighters[i] != null) && (fighters[i].curHp > 0)) {
                    targets.add(fighters[i]);
                }
            }
        }

        return targets;
    }

    public LinkedList<Fighter> getTargetsInColumn(Fighter fighter) {
        LinkedList<Fighter> targets = new LinkedList<Fighter>();

        switch (fighter.id) {
            case 0: 
            case 3: {
                if ((fighters[0] != null) && (fighters[0].curHp > 0)) {
                    targets.add(fighters[0]);
                }
                if ((fighters[3] != null) && (fighters[3].curHp > 0)) {
                    targets.add(fighters[3]);
                }
                break;
            }
            case 1:
            case 4: {
                if ((fighters[1] != null) && (fighters[1].curHp > 0)) {
                    targets.add(fighters[1]);
                }
                if ((fighters[4] != null) && (fighters[4].curHp > 0)) {
                    targets.add(fighters[4]);
                }
                break;
            }
            case 2:
            case 5: {
                if ((fighters[2] != null) && (fighters[2].curHp > 0)) {
                    targets.add(fighters[2]);
                }
                if ((fighters[5] != null) && (fighters[5].curHp > 0)) {
                    targets.add(fighters[5]);
                }
                break;
            }
            case 6:
            case 9: {
                if ((fighters[6] != null) && (fighters[6].curHp > 0)) {
                    targets.add(fighters[6]);
                }
                if ((fighters[9] != null) && (fighters[9].curHp > 0)) {
                    targets.add(fighters[9]);
                }
                break;
            }
            case 7:
            case 10: {
                if ((fighters[7] != null) && (fighters[7].curHp > 0)) {
                    targets.add(fighters[7]);
                }
                if ((fighters[10] != null) && (fighters[10].curHp > 0)) {
                    targets.add(fighters[10]);
                }
                break;
            }
            case 8:
            case 11: {
                if ((fighters[8] != null) && (fighters[8].curHp > 0)) {
                    targets.add(fighters[8]);
                }
                if ((fighters[11] != null) && (fighters[11].curHp > 0)) {
                    targets.add(fighters[11]);
                }
                break;
            }
        }

        return targets;
    }

    public Fighter getTarget(Fighter fighter, int targetChooseType) {
        switch (targetChooseType) {
            case Consts.SKILL_TARGET_CHOOSETYPE_FRONT_SINGLE_ENEMY: {
                return fighter.battle.getFrontEnemy(fighter);
            }
            case Consts.SKILL_TARGET_CHOOSETYPE_BACK_SINGLE_ENEMY: {
                return fighter.battle.getBackEnemy(fighter);
            }
            case Consts.SKILL_TARGET_CHOOSETYPE_MIN_HP_PERCENT_ENEMY: {
                return fighter.battle.getMinHpPercentFighter(fighter, true);
            }
            case Consts.SKILL_TARGET_CHOOSETYPE_MIN_HP_PERCENT_FRIEND: {
                return fighter.battle.getMinHpPercentFighter(fighter, false);
            }
            case Consts.SKILL_TARGET_CHOOSETYPE_RANDOM_ENEMY: {
                return fighter.battle.getRandomFighter(fighter, true);
            }
            case Consts.SKILL_TARGET_CHOOSETYPE_RANDOM_FRIEND: {
                return fighter.battle.getRandomFighter(fighter, false);
            }
        }
        return null;
    }

    private Fighter getFrontEnemy(Fighter fighter) {
        switch (fighter.id) {
            case 0:
            case 3: {
                if ((fighters[6] != null) && (fighters[6].curHp > 0)) {
                    return fighters[6];
                } else if ((fighters[7] != null) && (fighters[7].curHp > 0)) {
                    return fighters[7];
                } else if ((fighters[8] != null) && (fighters[8].curHp > 0)) {
                    return fighters[8];
                } else if ((fighters[9] != null) && (fighters[9].curHp > 0)) {
                    return fighters[9];
                } else if ((fighters[10] != null) && (fighters[10].curHp > 0)) {
                    return fighters[10];
                } else if ((fighters[11] != null) && (fighters[11].curHp > 0)) {
                    return fighters[11];
                }
                break;
            }
            case 1:
            case 4: {
                if ((fighters[7] != null) && (fighters[7].curHp > 0)) {
                    return fighters[7];
                } else if ((fighters[6] != null) && (fighters[6].curHp > 0)) {
                    return fighters[6];
                } else if ((fighters[8] != null) && (fighters[8].curHp > 0)) {
                    return fighters[8];
                } else if ((fighters[10] != null) && (fighters[10].curHp > 0)) {
                    return fighters[10];
                } else if ((fighters[9] != null) && (fighters[9].curHp > 0)) {
                    return fighters[9];
                } else if ((fighters[11] != null) && (fighters[11].curHp > 0)) {
                    return fighters[11];
                }
                break;
            }
            case 2:
            case 5: {
                if ((fighters[8] != null) && (fighters[8].curHp > 0)) {
                    return fighters[8];
                } else if ((fighters[7] != null) && (fighters[7].curHp > 0)) {
                    return fighters[7];
                } else if ((fighters[6] != null) && (fighters[6].curHp > 0)) {
                    return fighters[6];
                } else if ((fighters[11] != null) && (fighters[11].curHp > 0)) {
                    return fighters[11];
                } else if ((fighters[10] != null) && (fighters[10].curHp > 0)) {
                    return fighters[10];
                } else if ((fighters[9] != null) && (fighters[9].curHp > 0)) {
                    return fighters[9];
                }
                break;
            }
            case 6:
            case 9: {
                if ((fighters[0] != null) && (fighters[0].curHp > 0)) {
                    return fighters[0];
                } else if ((fighters[1] != null) && (fighters[1].curHp > 0)) {
                    return fighters[1];
                } else if ((fighters[2] != null) && (fighters[2].curHp > 0)) {
                    return fighters[2];
                } else if ((fighters[3] != null) && (fighters[3].curHp > 0)) {
                    return fighters[3];
                } else if ((fighters[4] != null) && (fighters[4].curHp > 0)) {
                    return fighters[4];
                } else if ((fighters[5] != null) && (fighters[5].curHp > 0)) {
                    return fighters[5];
                }
                break;
            }
            case 7:
            case 10: {
                if ((fighters[1] != null) && (fighters[1].curHp > 0)) {
                    return fighters[1];
                } else if ((fighters[0] != null) && (fighters[0].curHp > 0)) {
                    return fighters[0];
                } else if ((fighters[2] != null) && (fighters[2].curHp > 0)) {
                    return fighters[2];
                } else if ((fighters[4] != null) && (fighters[4].curHp > 0)) {
                    return fighters[4];
                } else if ((fighters[3] != null) && (fighters[3].curHp > 0)) {
                    return fighters[3];
                } else if ((fighters[5] != null) && (fighters[5].curHp > 0)) {
                    return fighters[5];
                }
                break;
            }
            case 8:
            case 11: {
                if ((fighters[2] != null) && (fighters[2].curHp > 0)) {
                    return fighters[2];
                } else if ((fighters[1] != null) && (fighters[1].curHp > 0)) {
                    return fighters[1];
                } else if ((fighters[0] != null) && (fighters[0].curHp > 0)) {
                    return fighters[0];
                } else if ((fighters[5] != null) && (fighters[5].curHp > 0)) {
                    return fighters[5];
                } else if ((fighters[4] != null) && (fighters[4].curHp > 0)) {
                    return fighters[4];
                } else if ((fighters[3] != null) && (fighters[3].curHp > 0)) {
                    return fighters[3];
                }
                break;
            }
        }
        return null;
    }

    private Fighter getBackEnemy(Fighter fighter) {
        switch (fighter.id) {
            case 0:
            case 3: {
                if ((fighters[9] != null) && (fighters[9].curHp > 0)) {
                    return fighters[9];
                } else if ((fighters[10] != null) && (fighters[10].curHp > 0)) {
                    return fighters[10];
                } else if ((fighters[11] != null) && (fighters[11].curHp > 0)) {
                    return fighters[11];
                } else if ((fighters[6] != null) && (fighters[6].curHp > 0)) {
                    return fighters[6];
                } else if ((fighters[7] != null) && (fighters[7].curHp > 0)) {
                    return fighters[7];
                } else if ((fighters[8] != null) && (fighters[8].curHp > 0)) {
                    return fighters[8];
                }
                break;
            }
            case 1:
            case 4: {
                if ((fighters[10] != null) && (fighters[10].curHp > 0)) {
                    return fighters[10];
                } else if ((fighters[9] != null) && (fighters[9].curHp > 0)) {
                    return fighters[9];
                } else if ((fighters[11] != null) && (fighters[11].curHp > 0)) {
                    return fighters[11];
                } else if ((fighters[7] != null) && (fighters[7].curHp > 0)) {
                    return fighters[7];
                } else if ((fighters[6] != null) && (fighters[6].curHp > 0)) {
                    return fighters[6];
                } else if ((fighters[8] != null) && (fighters[8].curHp > 0)) {
                    return fighters[8];
                }
                break;
            }
            case 2:
            case 5: {
                if ((fighters[11] != null) && (fighters[11].curHp > 0)) {
                    return fighters[11];
                } else if ((fighters[10] != null) && (fighters[10].curHp > 0)) {
                    return fighters[10];
                } else if ((fighters[9] != null) && (fighters[9].curHp > 0)) {
                    return fighters[9];
                } else if ((fighters[8] != null) && (fighters[8].curHp > 0)) {
                    return fighters[8];
                } else if ((fighters[7] != null) && (fighters[7].curHp > 0)) {
                    return fighters[7];
                } else if ((fighters[6] != null) && (fighters[6].curHp > 0)) {
                    return fighters[6];
                }
                break;
            }
            case 6:
            case 9: {
                if ((fighters[3] != null) && (fighters[3].curHp > 0)) {
                    return fighters[3];
                } else if ((fighters[4] != null) && (fighters[4].curHp > 0)) {
                    return fighters[4];
                } else if ((fighters[5] != null) && (fighters[5].curHp > 0)) {
                    return fighters[5];
                } else if ((fighters[0] != null) && (fighters[0].curHp > 0)) {
                    return fighters[0];
                } else if ((fighters[1] != null) && (fighters[1].curHp > 0)) {
                    return fighters[1];
                } else if ((fighters[2] != null) && (fighters[2].curHp > 0)) {
                    return fighters[2];
                }
                break;
            }
            case 7:
            case 10: {
                if ((fighters[4] != null) && (fighters[4].curHp > 0)) {
                    return fighters[4];
                } else if ((fighters[3] != null) && (fighters[3].curHp > 0)) {
                    return fighters[3];
                } else if ((fighters[5] != null) && (fighters[5].curHp > 0)) {
                    return fighters[5];
                } else if ((fighters[1] != null) && (fighters[1].curHp > 0)) {
                    return fighters[1];
                } else if ((fighters[0] != null) && (fighters[0].curHp > 0)) {
                    return fighters[0];
                } else if ((fighters[2] != null) && (fighters[2].curHp > 0)) {
                    return fighters[2];
                }
                break;
            }
            case 8:
            case 11: {
                if ((fighters[5] != null) && (fighters[5].curHp > 0)) {
                    return fighters[5];
                } else if ((fighters[4] != null) && (fighters[4].curHp > 0)) {
                    return fighters[4];
                } else if ((fighters[3] != null) && (fighters[3].curHp > 0)) {
                    return fighters[3];
                } else if ((fighters[2] != null) && (fighters[2].curHp > 0)) {
                    return fighters[2];
                } else if ((fighters[1] != null) && (fighters[1].curHp > 0)) {
                    return fighters[1];
                } else if ((fighters[0] != null) && (fighters[0].curHp > 0)) {
                    return fighters[0];
                }
                break;
            }
        }
        return null;
    }

    private Fighter getMinHpPercentFighter(Fighter fighter, boolean needEnemy){
        Fighter target = null;

        long minHpPercent = Long.MAX_VALUE;

        int startId;
        if (needEnemy) {
            startId = (fighter.id<6)?6:0;
        } else {
            startId = (fighter.id<6)?0:6;
        }

        for (int i=0; i<6; i++) {
            Fighter fighter1 = fighters[startId + i];
            if ((fighter1 != null) && (fighter1.curHp > 0)) {
                long tmpHpPercent = 1000*fighter1.curHp/fighter1.maxHp;
                if (tmpHpPercent < minHpPercent) {
                    minHpPercent = tmpHpPercent;
                    target = fighter1;
                }
            }
        }

        return target;
    }

    private Fighter getRandomFighter(Fighter fighter, boolean needEnemy){
        ArrayList<Fighter> selectList = new ArrayList<Fighter>();

        int startId;
        if (needEnemy) {
            startId = (fighter.id<6)?6:0;
        } else {
            startId = (fighter.id<6)?0:6;
        }

        for (int i=0; i<6; i++) {
            Fighter fighter1 = fighters[startId + i];
            if ((fighter1 != null) && (fighter1.curHp > 0)) {
                selectList.add(fighter1);
            }
        }

        int selectListSize = selectList.size();
        if (selectListSize == 0) {
            return null;
        } else if (selectListSize == 1) {
            return selectList.get(0);
        } else {
            return selectList.get(RandomGenerator.INSTANCE.generator.nextInt(selectListSize));
        }
    }

    public BattleMessageProto buildBattleMessageProto(int msgType, ByteString data) {
        BattleMessageProto.Builder builder = BattleMessageProto.newBuilder();
        builder.setType(msgType);

        if (data != null) {
            builder.setData(data);
        }

        return builder.build();
    }
}
